home *** CD-ROM | disk | FTP | other *** search
/ Freelog 115 / FreelogNo115-MaiJuin2013.iso / Internet / AvantBrowser / asetup.exe / _data / webkit / resources.pak / Unnamed File 000052.txt < prev    next >
Text File  |  2013-04-03  |  13KB  |  414 lines

  1. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4.  
  5. /**
  6.  * @fileoverview This is a data model representin
  7.  */
  8.  
  9. cr.define('cr.ui', function() {
  10.   /** @const */ var EventTarget = cr.EventTarget;
  11.   /** @const */ var Event = cr.Event;
  12.  
  13.   /**
  14.    * A data model that wraps a simple array and supports sorting by storing
  15.    * initial indexes of elements for each position in sorted array.
  16.    * @param {!Array} array The underlying array.
  17.    * @constructor
  18.    * @extends {EventTarget}
  19.    */
  20.   function ArrayDataModel(array) {
  21.     this.array_ = array;
  22.     this.indexes_ = [];
  23.     this.compareFunctions_ = {};
  24.  
  25.     for (var i = 0; i < array.length; i++) {
  26.       this.indexes_.push(i);
  27.     }
  28.   }
  29.  
  30.   ArrayDataModel.prototype = {
  31.     __proto__: EventTarget.prototype,
  32.  
  33.     /**
  34.      * The length of the data model.
  35.      * @type {number}
  36.      */
  37.     get length() {
  38.       return this.array_.length;
  39.     },
  40.  
  41.     /**
  42.      * Returns the item at the given index.
  43.      * This implementation returns the item at the given index in the sorted
  44.      * array.
  45.      * @param {number} index The index of the element to get.
  46.      * @return {*} The element at the given index.
  47.      */
  48.     item: function(index) {
  49.       if (index >= 0 && index < this.length)
  50.         return this.array_[this.indexes_[index]];
  51.       return undefined;
  52.     },
  53.  
  54.     /**
  55.      * Returns compare function set for given field.
  56.      * @param {string} field The field to get compare function for.
  57.      * @return {function(*, *): number} Compare function set for given field.
  58.      */
  59.     compareFunction: function(field) {
  60.       return this.compareFunctions_[field];
  61.     },
  62.  
  63.     /**
  64.      * Sets compare function for given field.
  65.      * @param {string} field The field to set compare function.
  66.      * @param {function(*, *): number} Compare function to set for given field.
  67.      */
  68.     setCompareFunction: function(field, compareFunction) {
  69.       if (!this.compareFunctions_) {
  70.         this.compareFunctions_ = {};
  71.       }
  72.       this.compareFunctions_[field] = compareFunction;
  73.     },
  74.  
  75.     /**
  76.      * Returns true if the field has a compare function.
  77.      * @param {string} field The field to check.
  78.      * @return {boolean} True if the field is sortable.
  79.      */
  80.     isSortable: function(field) {
  81.       return this.compareFunctions_ && field in this.compareFunctions_;
  82.     },
  83.  
  84.     /**
  85.      * Returns current sort status.
  86.      * @return {!Object} Current sort status.
  87.      */
  88.     get sortStatus() {
  89.       if (this.sortStatus_) {
  90.         return this.createSortStatus(
  91.             this.sortStatus_.field, this.sortStatus_.direction);
  92.       } else {
  93.         return this.createSortStatus(null, null);
  94.       }
  95.     },
  96.  
  97.     /**
  98.      * Returns the first matching item.
  99.      * @param {*} item The item to find.
  100.      * @param {number=} opt_fromIndex If provided, then the searching start at
  101.      *     the {@code opt_fromIndex}.
  102.      * @return {number} The index of the first found element or -1 if not found.
  103.      */
  104.     indexOf: function(item, opt_fromIndex) {
  105.       for (var i = opt_fromIndex || 0; i < this.indexes_.length; i++) {
  106.         if (item === this.item(i))
  107.           return i;
  108.       }
  109.       return -1;
  110.     },
  111.  
  112.     /**
  113.      * Returns an array of elements in a selected range.
  114.      * @param {number=} opt_from The starting index of the selected range.
  115.      * @param {number=} opt_to The ending index of selected range.
  116.      * @return {Array} An array of elements in the selected range.
  117.      */
  118.     slice: function(opt_from, opt_to) {
  119.       var arr = this.array_;
  120.       return this.indexes_.slice(opt_from, opt_to).map(
  121.           function(index) { return arr[index] });
  122.     },
  123.  
  124.     /**
  125.      * This removes and adds items to the model.
  126.      * This dispatches a splice event.
  127.      * This implementation runs sort after splice and creates permutation for
  128.      * the whole change.
  129.      * @param {number} index The index of the item to update.
  130.      * @param {number} deleteCount The number of items to remove.
  131.      * @param {...*} The items to add.
  132.      * @return {!Array} An array with the removed items.
  133.      */
  134.     splice: function(index, deleteCount, var_args) {
  135.       var addCount = arguments.length - 2;
  136.       var newIndexes = [];
  137.       var deletePermutation = [];
  138.       var deletedItems = [];
  139.       var newArray = [];
  140.       index = Math.min(index, this.indexes_.length);
  141.       deleteCount = Math.min(deleteCount, this.indexes_.length - index);
  142.       // Copy items before the insertion point.
  143.       for (var i = 0; i < index; i++) {
  144.         newIndexes.push(newArray.length);
  145.         deletePermutation.push(i);
  146.         newArray.push(this.array_[this.indexes_[i]]);
  147.       }
  148.       // Delete items.
  149.       for (; i < index + deleteCount; i++) {
  150.         deletePermutation.push(-1);
  151.         deletedItems.push(this.array_[this.indexes_[i]]);
  152.       }
  153.       // Insert new items instead deleted ones.
  154.       for (var j = 0; j < addCount; j++) {
  155.         newIndexes.push(newArray.length);
  156.         newArray.push(arguments[j + 2]);
  157.       }
  158.       // Copy items after the insertion point.
  159.       for (; i < this.indexes_.length; i++) {
  160.         newIndexes.push(newArray.length);
  161.         deletePermutation.push(i - deleteCount + addCount);
  162.         newArray.push(this.array_[this.indexes_[i]]);
  163.       }
  164.  
  165.       this.indexes_ = newIndexes;
  166.  
  167.       this.array_ = newArray;
  168.  
  169.       // TODO(arv): Maybe unify splice and change events?
  170.       var spliceEvent = new Event('splice');
  171.       spliceEvent.removed = deletedItems;
  172.       spliceEvent.added = Array.prototype.slice.call(arguments, 2);
  173.  
  174.       var status = this.sortStatus;
  175.       // if sortStatus.field is null, this restores original order.
  176.       var sortPermutation = this.doSort_(this.sortStatus.field,
  177.                                          this.sortStatus.direction);
  178.       if (sortPermutation) {
  179.         var splicePermutation = deletePermutation.map(function(element) {
  180.           return element != -1 ? sortPermutation[element] : -1;
  181.         });
  182.         this.dispatchPermutedEvent_(splicePermutation);
  183.         spliceEvent.index = sortPermutation[index];
  184.       } else {
  185.         this.dispatchPermutedEvent_(deletePermutation);
  186.         spliceEvent.index = index;
  187.       }
  188.  
  189.       this.dispatchEvent(spliceEvent);
  190.  
  191.       // If real sorting is needed, we should first call prepareSort (data may
  192.       // change), and then sort again.
  193.       // Still need to finish the sorting above (including events), so
  194.       // list will not go to inconsistent state.
  195.       if (status.field)
  196.         this.delayedSort_(status.field, status.direction);
  197.  
  198.       return deletedItems;
  199.     },
  200.  
  201.     /**
  202.      * Appends items to the end of the model.
  203.      *
  204.      * This dispatches a splice event.
  205.      *
  206.      * @param {...*} The items to append.
  207.      * @return {number} The new length of the model.
  208.      */
  209.     push: function(var_args) {
  210.       var args = Array.prototype.slice.call(arguments);
  211.       args.unshift(this.length, 0);
  212.       this.splice.apply(this, args);
  213.       return this.length;
  214.     },
  215.  
  216.     /**
  217.      * Use this to update a given item in the array. This does not remove and
  218.      * reinsert a new item.
  219.      * This dispatches a change event.
  220.      * This runs sort after updating.
  221.      * @param {number} index The index of the item to update.
  222.      */
  223.     updateIndex: function(index) {
  224.       if (index < 0 || index >= this.length)
  225.         throw Error('Invalid index, ' + index);
  226.  
  227.       // TODO(arv): Maybe unify splice and change events?
  228.       var e = new Event('change');
  229.       e.index = index;
  230.       this.dispatchEvent(e);
  231.  
  232.       if (this.sortStatus.field) {
  233.         var status = this.sortStatus;
  234.         var sortPermutation = this.doSort_(this.sortStatus.field,
  235.                                            this.sortStatus.direction);
  236.         if (sortPermutation)
  237.           this.dispatchPermutedEvent_(sortPermutation);
  238.         // We should first call prepareSort (data may change), and then sort.
  239.         // Still need to finish the sorting above (including events), so
  240.         // list will not go to inconsistent state.
  241.         this.delayedSort_(status.field, status.direction);
  242.       }
  243.     },
  244.  
  245.     /**
  246.      * Creates sort status with given field and direction.
  247.      * @param {string} field Sort field.
  248.      * @param {string} direction Sort direction.
  249.      * @return {!Object} Created sort status.
  250.      */
  251.     createSortStatus: function(field, direction) {
  252.       return {
  253.         field: field,
  254.         direction: direction
  255.       };
  256.     },
  257.  
  258.     /**
  259.      * Called before a sort happens so that you may fetch additional data
  260.      * required for the sort.
  261.      *
  262.      * @param {string} field Sort field.
  263.      * @param {function()} callback The function to invoke when preparation
  264.      *     is complete.
  265.      */
  266.     prepareSort: function(field, callback) {
  267.       callback();
  268.     },
  269.  
  270.     /**
  271.      * Sorts data model according to given field and direction and dispathes
  272.      * sorted event with delay. If no need to delay, use sort() instead.
  273.      * @param {string} field Sort field.
  274.      * @param {string} direction Sort direction.
  275.      * @private
  276.      */
  277.     delayedSort_: function(field, direction) {
  278.       var self = this;
  279.       setTimeout(function() {
  280.         // If the sort status has been changed, sorting has already done
  281.         // on the change event.
  282.         if (field == self.sortStatus.field &&
  283.             direction == self.sortStatus.direction) {
  284.           self.sort(field, direction);
  285.         }
  286.       }, 0);
  287.     },
  288.  
  289.     /**
  290.      * Sorts data model according to given field and direction and dispathes
  291.      * sorted event.
  292.      * @param {string} field Sort field.
  293.      * @param {string} direction Sort direction.
  294.      */
  295.     sort: function(field, direction) {
  296.       var self = this;
  297.  
  298.       this.prepareSort(field, function() {
  299.         var sortPermutation = self.doSort_(field, direction);
  300.         if (sortPermutation)
  301.           self.dispatchPermutedEvent_(sortPermutation);
  302.         self.dispatchSortEvent_();
  303.       });
  304.     },
  305.  
  306.     /**
  307.      * Sorts data model according to given field and direction.
  308.      * @param {string} field Sort field.
  309.      * @param {string} direction Sort direction.
  310.      * @private
  311.      */
  312.     doSort_: function(field, direction) {
  313.       var compareFunction = this.sortFunction_(field, direction);
  314.       var positions = [];
  315.       for (var i = 0; i < this.length; i++) {
  316.         positions[this.indexes_[i]] = i;
  317.       }
  318.       var sorted = this.indexes_.every(function(element, index, array) {
  319.         return index == 0 || compareFunction(element, array[index - 1]) >= 0;
  320.       });
  321.       if (!sorted)
  322.         this.indexes_.sort(compareFunction);
  323.       this.sortStatus_ = this.createSortStatus(field, direction);
  324.       var sortPermutation = [];
  325.       var changed = false;
  326.       for (var i = 0; i < this.length; i++) {
  327.         if (positions[this.indexes_[i]] != i)
  328.           changed = true;
  329.         sortPermutation[positions[this.indexes_[i]]] = i;
  330.       }
  331.       if (changed)
  332.         return sortPermutation;
  333.       return null;
  334.     },
  335.  
  336.     dispatchSortEvent_: function() {
  337.       var e = new Event('sorted');
  338.       this.dispatchEvent(e);
  339.     },
  340.  
  341.     dispatchPermutedEvent_: function(permutation) {
  342.       var e = new Event('permuted');
  343.       e.permutation = permutation;
  344.       e.newLength = this.length;
  345.       this.dispatchEvent(e);
  346.     },
  347.  
  348.     /**
  349.      * Creates compare function for the field.
  350.      * Returns the function set as sortFunction for given field
  351.      * or default compare function
  352.      * @param {string} field Sort field.
  353.      * @param {function(*, *): number} Compare function.
  354.      * @private
  355.      */
  356.     createCompareFunction_: function(field) {
  357.       var compareFunction =
  358.           this.compareFunctions_ ? this.compareFunctions_[field] : null;
  359.       var defaultValuesCompareFunction = this.defaultValuesCompareFunction;
  360.       if (compareFunction) {
  361.         return compareFunction;
  362.       } else {
  363.         return function(a, b) {
  364.           return defaultValuesCompareFunction.call(null, a[field], b[field]);
  365.         }
  366.       }
  367.       return compareFunction;
  368.     },
  369.  
  370.     /**
  371.      * Creates compare function for given field and direction.
  372.      * @param {string} field Sort field.
  373.      * @param {string} direction Sort direction.
  374.      * @param {function(*, *): number} Compare function.
  375.      * @private
  376.      */
  377.     sortFunction_: function(field, direction) {
  378.       var compareFunction = null;
  379.       if (field !== null)
  380.         compareFunction = this.createCompareFunction_(field);
  381.       var dirMultiplier = direction == 'desc' ? -1 : 1;
  382.  
  383.       return function(index1, index2) {
  384.         var item1 = this.array_[index1];
  385.         var item2 = this.array_[index2];
  386.  
  387.         var compareResult = 0;
  388.         if (typeof(compareFunction) === 'function')
  389.           compareResult = compareFunction.call(null, item1, item2);
  390.         if (compareResult != 0)
  391.           return dirMultiplier * compareResult;
  392.         return dirMultiplier * this.defaultValuesCompareFunction(index1,
  393.                                                                  index2);
  394.       }.bind(this);
  395.     },
  396.  
  397.     /**
  398.      * Default compare function.
  399.      */
  400.     defaultValuesCompareFunction: function(a, b) {
  401.       // We could insert i18n comparisons here.
  402.       if (a < b)
  403.         return -1;
  404.       if (a > b)
  405.         return 1;
  406.       return 0;
  407.     }
  408.   };
  409.  
  410.   return {
  411.     ArrayDataModel: ArrayDataModel
  412.   };
  413. });
  414.